﻿using System;
using System.Collections.Generic;
using System.Text;
using AgilePlannerDT.Display;
using AgilePlannerDT.Display.Events;
using AgilePlannerDT.Data;
using System.ComponentModel;
using System.Windows.Input;
using AgilePlannerDT.Application.Commands.Impl;
using System.Windows;
using System.Windows.Ink;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Windows.Threading;
using AgilePlannerDT.Data.Impl;
using AgilePlannerDT.Persister.Impl;    

namespace AgilePlannerDT.Application.Modules.Impl
{
    public class IndexCardEventHandlerModuleImpl : AbstractApplicationModule, IndexCardEventHandlerModule
    {
        #region Attributes

        protected Stroke gestureStroke;
        protected Canvas windowCanvas;
        protected GestureModule gestureModule;

        #endregion

        #region Constructors

        public IndexCardEventHandlerModuleImpl(AgilePlannerApplication app, Dispatcher dispatcher, Canvas windowCanvas, GestureModule gestureModule)
        {
            this.app = app;
            this.dispatcher = dispatcher;
            this.windowCanvas = windowCanvas;
            this.gestureModule = gestureModule;
        }

        #endregion

        #region IndexCardEventHandlerModule Members

        public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
        {
            if (sender is Iteration)
                IterationData_PropertyChanged((Iteration)sender, e);
            else if (sender is StoryCard)
                StoryCardData_PropertyChanged((StoryCard)sender, e);
        }

        public void PreviewMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e)
        {
            if (sender is IterationDisplayObject)
                Iteration_PreviewMouseLeftButtonDown((IterationDisplayObject)sender, e);
            else if (sender is StoryCardDisplayObject)
                StoryCard_PreviewMouseLeftButtonDown((StoryCardDisplayObject)sender, e);
        }

        public void PreviewMouseLeftButtonUpHandler(object sender, MouseButtonEventArgs e)
        {
            if (sender is IterationDisplayObject)
                Iteration_PreviewMouseLeftButtonUp((IterationDisplayObject)sender, e);
            else if (sender is StoryCardDisplayObject)
                StoryCard_PreviewMouseLeftButtonUp((StoryCardDisplayObject)sender, e);
        }

        public void PreviewMouseMoveHandler(object sender, MouseEventArgs e)
        {
            if (sender is IterationDisplayObject)
                Iteration_PreviewMouseMove((IterationDisplayObject)sender, e);
            else if (sender is StoryCardDisplayObject)
                StoryCard_PreviewMouseMove((StoryCardDisplayObject)sender, e);
        }

        public void IndexCardDropHandler(DisplayObject sender, IndexCardDropEventArgs e)
        {
            //TODO check if DataObject and DisplayObject are at the same coordinates
            //if so -> do nothing

            if (sender is IterationDisplayObject)
                Iteration_OnIndexCardDropped((IterationDisplayObject)sender, e);
            else if (sender is StoryCardDisplayObject)
                StoryCard_OnIndexCardDropped((StoryCardDisplayObject)sender, e);
        }

        #endregion

        #region Helper Methods


        #region StoryCards

        private void StoryCardData_PropertyChanged(StoryCard sender, PropertyChangedEventArgs e)
        {
            if (!Application.IsLoadingPhase)
            {
                if (e is IndexCardPropertyChangedEventArgs)
                {
                    IndexCardPropertyChangedEventArgs i = (IndexCardPropertyChangedEventArgs)e;

                    Application.Persister.UpdateStoryCard((StoryCard)i.After);
                }
            }
        }

        private void StoryCard_PreviewMouseLeftButtonDown(StoryCardDisplayObject sender, MouseButtonEventArgs e)
        {
            //one of the storycards has been clicked
            Application.ActiveCard = sender;
            Application.SelectedCard = sender;

            //start a new gesture stroke
            //if (inkPresenter == null)
            //{
            //    inkPresenter = new InkPresenter();
            //    windowCanvas.Children.Add(inkPresenter);
            //}

            //gestureStroke = new Stroke(new StylusPointCollection(new Point[] { position }));
            //inkPresenter.Strokes.Add(gestureStroke);
        }

        private void StoryCard_PreviewMouseLeftButtonUp(StoryCardDisplayObject sender, MouseButtonEventArgs e)
        {
            Application.ActiveCard = null;

            Point position = e.GetPosition(windowCanvas);
            StrokeCollection strokesToRecognize = new StrokeCollection();

            if (gestureStroke != null)
            {
                gestureStroke.StylusPoints.Add(new StylusPoint(position.X, position.Y));
                strokesToRecognize.Add(gestureStroke);
            }

            ReadOnlyCollection<GestureRecognitionResult> results = gestureModule.GestureRecognizer.Recognize(strokesToRecognize);

            if (results.Count > 0)
            {
                gestureModule.ProcessGesture(sender, new InkCanvasGestureEventArgs(strokesToRecognize, results));
            }

            //inkPresenter.Strokes.Clear();
            gestureStroke = null;
        }

        private void StoryCard_PreviewMouseMove(StoryCardDisplayObject sender, MouseEventArgs e)
        {
            Point position = e.GetPosition(windowCanvas);

            if (gestureStroke != null)
            {
                gestureStroke.StylusPoints.Add(new StylusPoint(position.X, position.Y));
            }
        }

        private void StoryCard_OnIndexCardDropped(StoryCardDisplayObject sender, IndexCardDropEventArgs e)
        {
            //if display object already is at the right place -> do nothing because this
            //event was triggered by an incoming move that was already taken care of

            //don't use the double values for comparison, they are almost always a bit off
            if (!((int)sender.LocationX == (int)(sender.StoryCardData.LocationX*Properties.Settings.Default.Scale) &&
                (int)sender.LocationY == (int)(sender.StoryCardData.LocationY*Properties.Settings.Default.Scale) &&
                (int)sender.RotationAngle == (int)sender.StoryCardData.RotationAngle))
            {

                //5 cases:
                //card stays inside backlog -> update
                //card stays inside iteration -> update
                //move from backlog to iteration -> move to new parent + update
                //move from iteration to backlog -> move to new parent + update
                //move from one iteration to another -> move to new parent + update
                //... move to new parent should contain rotationAngle as parameter!

                IterationDisplayObject oldparent, newParent;
                StoryCardDisplayObject storycard = (StoryCardDisplayObject)sender;
                //change the dataobject layer
//sender.StoryCardData.LocationX = sender.LocationX / Properties.Settings.Default.Scale;
//                sender.StoryCardData.LocationY = sender.LocationY/Properties.Settings.Default.
                if (Application.Project == null)
                {
                    MessageBox.Show("You are not connected to a backend! Cannot complete StoryCard_OnIndexCardDropped", "Error");
                }
                //new parent is iteration
                else if (Application.TryFindIteration(e.ReleasePoint, out newParent))
                {
                    //same iteration as before
                    if (newParent.ID == storycard.ParentID)
                    {
                   //     Application.Persister.UpdateStoryCard(e.After);
                        Application.Persister.UpdateStoryCard((StoryCard)e.After);
                    }
                    //came from different iteration
                    else if (storycard.ParentID != Application.Project.Backlog.ID)
                    {
                        DisplayObject dobj;
                        Application.Cards.TryGetValue(storycard.ParentID, out dobj);
                        oldparent = (IterationDisplayObject)dobj;

                        //removal is done on callback from persiste
                        //attention please: if this is local call, the parentID will be changed while updating card, so, we can not wait for onMoveParent event to remove the card from parentdisplay object
                        if (this.app.Persister is AsyncWrapperPersister)
                           oldparent.RemoveStoryCard(storycard);

                        //Application.Persister.MoveStoryCardToNewParent(storycard.StoryCardData, newParent.ID,
                        //    (int)storycard.LocationX, (int)storycard.LocationY, (float)storycard.RotationAngle);
                        Application.Persister.MoveStoryCardToNewParent((StoryCard)e.After, newParent.ID,
                            (int)((StoryCard)e.After).LocationX, (int)((StoryCard)e.After).LocationY, (float)((StoryCard)e.After).RotationAngle);
                    }
                    //came from backlog
                    else
                    {
                        //no representation for backlog so we don't
                        //have to remove the card from oldParent
                        //Application.Persister.MoveStoryCardToNewParent(storycard.StoryCardData, newParent.ID,
                        //    (int)storycard.LocationX, (int)storycard.LocationY, (float)storycard.RotationAngle);
                        Application.Persister.MoveStoryCardToNewParent(((StoryCard)e.After), newParent.ID,
                            (int)((StoryCard)e.After).LocationX, (int)((StoryCard)e.After).LocationY, (float)((StoryCard)e.After).RotationAngle);
                    }

                }
                //new parent is backlog
                else
                {
                    //was backlog before
                    if (storycard.ParentID == Application.Project.Backlog.ID)
                    {
                        //Application.Persister.UpdateStoryCard(storycard.StoryCardData);
                        Application.Persister.UpdateStoryCard((StoryCard)e.After);
                    }
                    //moved from iteration to backlog
                    else
                    {
                        //no representation for backlog so we don't
                        //have to add the card to newParent
                        DisplayObject dobj;
                        Application.Cards.TryGetValue(storycard.ParentID, out dobj);

             //           if(this.app.Persister is )
                        oldparent = (IterationDisplayObject)dobj;
                        //attention please: if this is local call, the parentID will be changed while updating card, so, we can not wait for onMoveParent event to remove the card from parentdisplay object
                        if (this.app.Persister is AsyncWrapperPersister)
                        oldparent.RemoveStoryCard(storycard);

                        //Application.Persister.MoveStoryCardToNewParent(storycard.StoryCardData, Application.Project.Backlog.ID,
                        //    (int)storycard.LocationX, (int)storycard.LocationY, (float)storycard.RotationAngle);
                        Application.Persister.MoveStoryCardToNewParent(((StoryCard)e.After), Application.Project.Backlog.ID,
                            (int)((StoryCard)e.After).LocationX, (int)((StoryCard)e.After).LocationY, (float)((StoryCard)e.After).RotationAngle);
                    }
                }
            }
        }

        #endregion

        #region Iterations

        private void Iteration_OnIndexCardDropped(IterationDisplayObject sender, IndexCardDropEventArgs e)
        {
            double offsetX = ((Iteration)e.After).LocationX - ((Iteration)e.Before).LocationX;
            double offsetY = ((Iteration)e.After).LocationY - ((Iteration)e.Before).LocationY;
            double offsetR = ((Iteration)e.After).RotationAngle - ((Iteration)e.Before).RotationAngle;
            //change the location X and Y in the data object layer
            sender.IterationData.LocationX += offsetX;
            sender.IterationData.LocationY += offsetY;
            foreach (StoryCard sc in sender.IterationData.StoryCardChildren)
            {
                sc.LocationX     += offsetX;
                sc.LocationY     += offsetY;
                sc.RotationAngle += offsetR;

    //            Application.Persister.UpdateStoryCard((StoryCard)sc.Clone());
            }

            Application.Persister.UpdateIteration(sender.IterationData);
        }

        private void IterationData_PropertyChanged(Iteration sender, PropertyChangedEventArgs e)
        {
            if (!Application.IsLoadingPhase)
            {
                if (e is IndexCardPropertyChangedEventArgs)
                {
                    IndexCardPropertyChangedEventArgs i = (IndexCardPropertyChangedEventArgs)e;
                    Application.Persister.UpdateIteration((Iteration)i.After);
                }
            }
        }

        private void Iteration_PreviewMouseMove(IterationDisplayObject sender, MouseEventArgs e)
        {
            Point position = e.GetPosition(windowCanvas);

            if (gestureStroke != null)
            {
                gestureStroke.StylusPoints.Add(new StylusPoint(position.X, position.Y));
            }
        }

        private void Iteration_PreviewMouseLeftButtonUp(IterationDisplayObject sender, MouseButtonEventArgs e)
        {
            Application.ActiveCard = null;

            Point position = e.GetPosition(windowCanvas);

            StrokeCollection strokesToRecognize = new StrokeCollection();

            if (gestureStroke != null)
            {
                gestureStroke.StylusPoints.Add(new StylusPoint(position.X, position.Y));
                strokesToRecognize.Add(gestureStroke);
            }

            //if (gestureStroke != null)
            //{
            //    gestureStroke.StylusPoints.Add(new StylusPoint(position.X, position.Y));
            //}

            //StrokeCollection strokesToRecognize = new StrokeCollection();
            //strokesToRecognize.Add(gestureStroke);
            ReadOnlyCollection<GestureRecognitionResult> results = gestureModule.GestureRecognizer.Recognize(strokesToRecognize);

            if (results.Count > 0)
            {
                gestureModule.ProcessGesture(sender, new InkCanvasGestureEventArgs(strokesToRecognize, results));
            }

            //inkPresenter.Strokes.Clear();
            gestureStroke = null;
        }

        private void Iteration_PreviewMouseLeftButtonDown(IterationDisplayObject sender, MouseButtonEventArgs e)
        {
            Application.ActiveCard = (IterationDisplayObject)sender;
            Application.SelectedCard = (IterationDisplayObject)sender;

            //start a new gesture stroke
            //if (inkPresenter == null)
            //{
            //    inkPresenter = new InkPresenter();
            //    windowCanvas.Children.Add(inkPresenter);
            //}

            Point position = e.GetPosition(windowCanvas);
            gestureStroke = new Stroke(new StylusPointCollection(new Point[] { position }));
            //inkPresenter.Strokes.Add(gestureStroke);
        }

        #endregion

       
        #endregion
    }
}
